<?php
namespace addons\paotui\service;

use addons\paotui\library\Convert;
use app\admin\model\paotui\PostBeatSet;
use app\admin\model\paotui\PostDispatch;
use app\admin\model\paotui\PostDispatchSet;
use app\admin\model\paotui\Poster;
use app\admin\model\paotui\Posterlevel;
use app\admin\model\paotui\PostSocket;
use GatewayWorker\Lib\Gateway;
use think\Db;

class Socket
{
    /**
     * 获取等待推送的订单
     */
    public static function handleMessage(){
        Db::startTrans();

		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----222\n",FILE_APPEND);
		// die;
			

        $postSocketModel = new PostSocket();
        $postSocketInfo = $postSocketModel
            ->where(['next_send_time'=>['elt',time()],'send_status'=>0])
            ->order("next_send_time asc")
            ->lock(true)
            ->find();
		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----2".json_encode($postSocketInfo)."\n",FILE_APPEND);
        if($postSocketInfo){
            $post = self::getPostInfo($postSocketInfo['post_id']);

            if(empty($post)){
                //订单不存在，删除推送记录
                $postSocketInfo->delete();
            }

            //推送消息
            $result = self::switchSendTime($postSocketInfo,$post);
		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----3".json_encode($result)."\n",FILE_APPEND);
            if($result){

                self::sendMessage($postSocketInfo,$post,empty($result['poster_id'])?"":$result['poster_id'],empty($result['uninc_poster_id'])?"":$result['uninc_poster_id'],empty($result['poster_num'])?"":$result['poster_num'],empty($result['distance'])?"":$result['distance']);

                $postSocketInfo->allowField(true)->save($result);
            }else{
                $postSocketInfo->save(['send_status'=>1]);
            }

        }
        Db::commit();
    }

    /**
     * 格式化推送消息
     * @param $postSocketInfo
     * @param $post
     * @throws \think\Exception
     */
    private static function sendMessage($postSocketInfo,$post,$poster_id,$uninc_poster_id,$poster_num,$distance){

        $message = [
            'type'=>'new_order',
            'mode'=>$postSocketInfo['type'],
            'post_id'=>$post['id'],
            'order_type'=>$post['type'],
            'order_no'=>$post['order_no'],
            'from_province'=>$post['from_province'],
            'from_city'=>$post['from_city'],
            'from_district'=>$post['from_district'],
            'from_poi'=>$post['from_poi'],
            'from_lat'=>$post['from_lat'],
            'from_lng'=>$post['from_lng'],
            'from_address'=>$post['from_address'],
            'to_province'=>$post['to_province'],
            'to_city'=>$post['to_city'],
            'to_district'=>$post['to_district'],
            'to_poi'=>$post['to_poi'],
            'to_lat'=>$post['to_lat'],
            'to_lng'=>$post['to_lng'],
            'to_address'=>$post['to_address'],
            'distance'=>$post['distance']."公里",
            'goods_type'=>$post['goods_type'],
            'weight'=>$post['weight']."公斤",
            'brokerage'=>$post['brokerage'],
            'tips_price'=>$post['tips_price'],
            'b_price'=>$post['b_price'],
            'extra_price'=>$post['extra_price'],
            'is_appoints'=>$post['is_appoints'],
            'appoints_day'=>$post['appoints_day'],
            'appoints_time'=>$post['appoints_time'],
            'note'=>$post['note']
        ];

        $message['audio'] = self::getAudio($postSocketInfo['type'],$postSocketInfo['type1'])."?t=".time();

        if($post['appoints_day']){
            $today = date("m-d");
            if($today == $post['appoints_day']){
                $message['appoints_day'] = "今天";
            }

            $tomorrow = date("m-d",strtotime("+1 day",time()));
            if($tomorrow == $post['appoints_day']){
                $message['appoints_day'] = "明天";
            }
        }
        //获取在线的快递员
        $posters = self::getOnlinePoster($postSocketInfo['type'],$post['from_lng'],$post['from_lat'],$distance,$poster_num,$poster_id,$uninc_poster_id,$post);
        if(!empty($posters)){
            foreach($posters as $k=>$v){
                if(empty($v['lat']) || empty($v['lng'])){
                    $from_distance = 0;
                    $to_distance = 0;
                }else{
                    $from_distance = round((Convert::distance($post['from_lat'],$post['from_lng'],$v['lat'],$v['lng']))/1000,1);
                    $to_distance = round((Convert::distance($post['to_lat'],$post['to_lng'],$v['lat'],$v['lng']))/1000,1);
                }

                $message['from_distance'] = $from_distance;
                $message['to_distance'] = $to_distance;
                $message['brokerage'] = Post::poster_brokerage($post['brokerage'],$post['type'],$v['poster_type'],$v['brokerage_type'],$v['brokerage_scale']);
                if(Gateway::isUidOnline($v['id'])){
                    Gateway::sendToUid($v['id'], json_encode($message));
                }
            }
        }

    }

    /**
     * 获取音频地址
     * @param $type
     * @return mixed
     */
    private static function getAudio($type,$type1=0){
        switch ($type){
            case "1":       //抢单
				if(!$type1){
					$audio =  Config::get("beat_mp3");
				}else{
					$audio =  Config::get("beatmp3_".$type1);
				}
                break;
            case "2":       //系统派单
                $audio =  Config::get("auto_dispatch_mp3");
                break;
            case "3":       //人工派单
                $audio =  Config::get("hand_dispatch_mp3");
                break;
            case "4":       //取消订单
                $audio =  Config::get("cancel_mp3");
                break;
        }
        return $audio;
    }

    /**
     * 获取下次推送时间
     */
    private static function switchSendTime($postSocketInfo,$post){

		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----5".json_encode($postSocketInfo)."\n",FILE_APPEND);
        switch ($postSocketInfo['type']){
            case 1:     //抢单
                $result = self::getBeatPostSendTime($postSocketInfo,$post);
                break;
            case 2:     //系统指派
                $result = self::getAutoDispatchSendTime($postSocketInfo,$post);
                break;
            case 3:     //人工指派
                $result = self::getHandDispatchSendTime($postSocketInfo,$post);
                break;
            case 4:     //取消订单
                $result = self::getCancelSendTime($postSocketInfo,$post);
                break;
            default:
                $result = null;
                break;
        }

        return $result;
    }

    /**
     * 获取订单详情
     * @param $post_id
     * @return array|bool|\PDOStatement|string|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    private static function getPostInfo($post_id){
        $postModel = new \app\admin\model\paotui\Post();
        $post=$postModel->where(['id'=>$post_id])->find();
		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----p1".json_encode($post)."\n",FILE_APPEND);
		if($post['from_address_id'] && !$post['from_lng']){
			$address=db('paotui_user_address')->where('id',$post['from_address_id'])->find();
			$post['from_lng']=$address['lng'];
			$post['from_lat']=$address['lat'];
		}
		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----p2".json_encode($post)."\n",FILE_APPEND);
		return $post;
    }

    /**
     * 获取抢单模式下的推送信息
     * @param $postSocketInfo
     */
    private static function getBeatPostSendTime($postSocketInfo,$post){

		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----4".json_encode($post)."\n",FILE_APPEND);
        if($post['status'] != 2){
            return null;
        }

        $postBeatSetModel = new PostBeatSet();
        $postBeatSetInfo = $postBeatSetModel->where(['level'=>['gt',$postSocketInfo['send_num']]])->order("level asc")->find();
		// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."----1".json_encode($postBeatSetInfo)."\n",FILE_APPEND);
        if($postBeatSetInfo){
            return ['send_num'=>$postSocketInfo['send_num']+1,'poster_num'=>$postBeatSetInfo['num'],'distance'=>$postBeatSetInfo['distance'],'next_send_time'=>time()+$postBeatSetInfo['seconds']];
        }else{
            return null;
        }

    }

    /**
     * 获取系统派单模式下的推送信息
     * @param $postSocketInfo
     * @param $post
     */
    private static function getAutoDispatchSendTime($postSocketInfo,$post){
        if($post['status'] != 9){
            return null;
        }

        if($postSocketInfo['send_num'] > 0){
            //超时接单
            $dispatchModel = new PostDispatch();
            $dispatchModel->where(['post_id'=>$post['id'],'poster_id'=>$postSocketInfo['poster_id'],'type'=>1,'status'=>0])->update(['status'=>-2]);
        }

        $postDispatchSetModel = new PostDispatchSet();
        $postDispatchSetInfo = $postDispatchSetModel->where(['level'=>['gt',$postSocketInfo['send_num']]])->order("level asc")->find();
        if($postDispatchSetInfo){
            $uninc_poster_id = empty($postDispatchSetInfo['uninc_poster_id'])?[]:explode(",",$postDispatchSetInfo['uninc_poster_id']);
            array_push($uninc_poster_id,$postDispatchSetInfo['poster_id']);
            return ['send_num'=>$postSocketInfo['send_num']+1,'poster_num'=>0,'distance'=>$postDispatchSetInfo['distance'],'uninc_poster_id'=>implode(",",$uninc_poster_id),'send_time'=>time()+$postDispatchSetInfo['seconds']];
        }else{
            //将订单设置为待抢单
            $postModel = new \app\admin\model\paotui\Post();
            $postModel->where(['id'=>$post['id'],'status'=>9])->update(['status'=>2,'poster_id'=>0]);
            return null;
        }


    }

    /**
     * 获取人工派单模式下的推送信息
     * @param $postSocketInfo
     * @param $post
     */
    private static function getHandDispatchSendTime($postSocketInfo,$post){

        if($post['status'] != 8){
            return null;
        }

        if($postSocketInfo['send_num'] == 0){
            return ['send_num'=>$postSocketInfo['send_num']+1,'distance'=>$postSocketInfo['distance'],'next_send_time'=>time()+Config::get('hand_dispatch_delay')];
        }else{
            //超时接单
            $dispatchModel = new PostDispatch();
            $dispatchModel->where(['post_id'=>$post['id'],'poster_id'=>$post['poster_id'],'type'=>2,'status'=>0])->update(['status'=>-2]);

            //将订单设置为待抢单
            $postModel = new \app\admin\model\paotui\Post();
            $postModel->where(['id'=>$post['id'],'status'=>8])->update(['status'=>2,'poster_id'=>0]);
            return null;
        }
    }

    /**
     * 获取取消订单的推送信息
     * @param $postSocketInfo
     * @param $post
     */
    private static function getCancelSendTime($postSocketInfo,$post){
        if($postSocketInfo['send_num'] > 60){
            return null;
        }

        //一分钟推送一次，直到主动关闭
        return ['send_num'=>$postSocketInfo['send_num']+1,'poster_id'=>$post['poster_id'],'distance'=>$postSocketInfo['distance'],'next_send_time'=>time()+60];
    }

    /**
     * 获取当前在线的接单中周边快递员
     */
    private static function getOnlinePoster($type,$lng="",$lat="",$distance,$poster_num,$poster_id,$uninc_poster_id,$postInfo){

		file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."l1----".json_encode($type)."\n",FILE_APPEND);
        $posterlevelModel = new Posterlevel();
        $levelMaxNum = $posterlevelModel->column("id,max_num");

        switch($type){
            case "1":       //抢单
                if(empty($poster_num)){
                    $limit = "";
                }else{
                    $limit = $poster_num;
                }

                $postModel = new Poster();
                $posterList = collection($postModel->alias("poster")
                    ->field("poster.*,location.lat,location.lng")
                    ->join("paotui_poster_location location","poster.id=location.poster_id","left")
                    ->where(['poster.status'=>1,'poster.is_order'=>1,'poster.is_online'=>1])
					//20240828 masks 暂时先去掉，有影响了再说
                    // ->where("","exp","(2 * 6378.137* ASIN(SQRT(POW(SIN(PI()*({$lng}-location.lng)/360),2)+COS(PI()*{$lat}/180)* COS(location.lat * PI()/180)*POW(SIN(PI()*({$lat}-location.lat)/360),2)))) < ".$distance)
                    ->limit($limit)
                    ->select())->toArray();
				// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')."l2----".json_encode($posterList)."\n",FILE_APPEND);
				// file_put_contents('./testapi.txt',date('Y-m-d H:i:s')." l3----".db('paotui_poster')->getLastSql()."\n",FILE_APPEND);
                if($posterList){
                    foreach ($posterList as $k=>$v){
                        $postModel = new \app\admin\model\paotui\Post();
                        $hasPostCount = $postModel->where(['poster_id'=>$v['id'],'status'=>['in',[2,3,4]]])->count();
                        if($hasPostCount >= (empty($levelMaxNum[$v['level_id']])?0:$levelMaxNum[$v['level_id']])){
                            unset($posterList[$k]);
                        }else{
                            $posterList[$k]['free_post_scale'] = ($levelMaxNum[$v['level_id']] == 0?0:(1-$hasPostCount/$levelMaxNum[$v['level_id']]));
                        }
                    }
                }
                break;
            case "2":       //系统派单
                $limit = 5;
                $whereUnincPosterId="";
                if($uninc_poster_id){
                    $whereUnincPosterId = ['id'=>['not in',$uninc_poster_id]];
                }

                $posterModel = new Poster();
                $_posterList = collection($posterModel->alias("poster")
                    ->field("poster.*,location.lat,location.lng")
                    ->join("paotui_poster_location location","poster.id=location.poster_id")
                    ->where(['poster.status'=>1,'poster.is_order'=>1,'poster.is_online'=>1])
                    ->where("","exp","(2 * 6378.137* ASIN(SQRT(POW(SIN(PI()*({$lng}-location.lng)/360),2)+COS(PI()*{$lat}/180)* COS(location.lat * PI()/180)*POW(SIN(PI()*({$lat}-location.lat)/360),2)))) < ".$distance)
                    ->where($whereUnincPosterId)
                    ->limit($limit)
                    ->select())->toArray();
                $posterList = [];
                if($_posterList){
                    foreach ($_posterList as $k=>$v){
                        $postModel = new \app\admin\model\paotui\Post();
                        $hasPostCount = $postModel->where(['poster_id'=>$v['id'],'status'=>['in',[2,3,4]]])->count();
                        if($hasPostCount >= (empty($levelMaxNum[$v['level_id']])?0:$levelMaxNum[$v['level_id']])){
                            unset($_posterList[$k]);
                        }else{
                            $_posterList[$k]['free_post_scale'] = ($levelMaxNum[$v['level_id']] == 0?0:(1-$hasPostCount/$levelMaxNum[$v['level_id']]));
                        }
                        $_posterList[$k]['score'] = self::getPosterScore($v,$postInfo);
                        if(!empty($_posterList)){
                            if($_posterList[$k]['score'] < $_posterList[$k-1]['score']){
                                $posterList[] = $v;
                            }
                        }else{
                            $posterList[] = $v;
                        }
                    }
                }
                if($posterList){
                    //更新当前系统推送的骑手信息
                    $postSocketModel = new PostSocket();
                    $postSocketModel->where(['post_id'=>$postInfo['id']])->update(['poster_id'=>$posterList[0]['id']]);

                    $postModel = new \app\admin\model\paotui\Post();
                    $postModel->where(['id'=>$postInfo['id'],'status'=>9])->update(['poster_id'=>$posterList[0]['id']]);
                }
                break;
            case "3":       //人工派单
                $wherePosterId = "";
                if($poster_id){
                    $wherePosterId = ['id'=>$poster_id];
                }

                $postModel = new Poster();
                $posterList =  collection($postModel->alias("poster")
                    ->field("poster.*,location.lat,location.lng")
                    ->join("paotui_poster_location location","poster.id=location.poster_id")
                    ->where(['poster.status'=>1,'poster.is_order'=>1,'poster.is_online'=>1])
                    ->where($wherePosterId)
                    ->select())->toArray();
                break;
            case "4":       //取消订单
                $wherePosterId = "";
                if($poster_id){
                    $wherePosterId = ['poster.id'=>$poster_id];
                }

                $postModel = new Poster();
                $posterList =  collection($postModel->alias("poster")
                    ->field("poster.*,location.lat,location.lng")
                    ->join("paotui_poster_location location","poster.id=location.poster_id")
                    ->where(['poster.status'=>1,'poster.is_order'=>1,'poster.is_online'=>1])
                    ->where($wherePosterId)
                    ->limit(1)
                    ->select())->toArray();
                break;

        }
        return $posterList;

    }

    /**
     * 获取骑手接单分数，排序选择分数平均值最小的骑手
     */
    private static function getPosterScore($posterInfo,$postInfo){
        //1.计算空闲单量比例
        $free_post_scale = $posterInfo['free_post_scale'];
        //2.计算当前骑手所有的进行中的订单最小角度
        $postModel = new \app\admin\model\paotui\Post();
        $selfPostList = $postModel->where(['poster_id'=>$posterInfo['poster_id'],'status'=>['in',[2,3,4]]])->select();
        if(empty($selfPostList)){
            $angle = 0;
        }else{
            $angle = 3;
            foreach ($selfPostList as $k=>$v){
                $_angle = Convert::GetAngle($postInfo['from_lng'],$postInfo['from_lat'],$postInfo['to_lng'],$postInfo['to_lat'],$v['to_lng'],$v['to_lat']);
                if($_angle<$angle){
                    $angle = $_angle;
                }
            }
        }
        //3.计算距离
        $distance = Convert::distance($postInfo['from_lat'],$postInfo['from_lng'],$posterInfo['lat'],$posterInfo['lng']);

        return $free_post_scale*30+$angle*40+$distance*30;
    }

}